package com.xwj.action.impl;

import com.xwj.action.StorageTccAction;
import com.xwj.dao.StorageMapper;
import com.xwj.dao.StorageTxLogMapper;
import com.xwj.domain.StorageTxLog;
import com.xwj.enums.TccTransactionalStatus;
import io.seata.rm.tcc.api.BusinessActionContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.Date;

/**
 * @author xwj
 * @date 2020/11/13
 */
@Component
@Slf4j
public class StorageTccActionImpl implements StorageTccAction {
    @Autowired
    private StorageMapper storageMapper;
    @Autowired
    private TransactionTemplate transactionTemplate;
    @Autowired
    private StorageTxLogMapper storageTxLogMapper;

    @Override
    public boolean decreaseTccAction(BusinessActionContext businessActionContext, final Long productId, final Integer count) {
        // 分布式事务ID
        String xid = businessActionContext.getXid();
        long branchId = businessActionContext.getBranchId();
        return transactionTemplate.execute(new TransactionCallback<Boolean>() {
            @Override
            public Boolean doInTransaction(TransactionStatus transactionStatus) {
                log.info("=========xid：{}，branchId：{}，执行Try操作", xid, branchId);
                try {
                    // 插入事务控制表记录，如果插入成功，说明第二阶段还没有执行，可以继续执行第一阶段。
                    // 如果插入失败，则说明第二阶段已经执行或正在执行，则抛出异常，终止即可
                    StorageTxLog storageTxLog = new StorageTxLog(xid, branchId, TccTransactionalStatus.TRY.getCode(), new Date(), new Date());
                    int result = storageTxLogMapper.insert(storageTxLog);
                    if (result != 1) {
                        return false;
                    }
                    result = storageMapper.decrease(productId, count);
                    if (result == 1) {
                        return true;
                    }
//                    throw new RuntimeException("库存不足");
                    return false;
                } catch (Exception e) {
                    transactionStatus.setRollbackOnly();
                    e.printStackTrace();
                    return false;
                }
            }
        });
    }

    @Override
    public boolean commitDecrease(BusinessActionContext businessActionContext) {
        // 分布式事务ID
        String xid = businessActionContext.getXid();
        long branchId = businessActionContext.getBranchId();
        return transactionTemplate.execute(new TransactionCallback<Boolean>() {
            @Override
            public Boolean doInTransaction(TransactionStatus transactionStatus) {
                log.info("=========xid：{}，branchId：{}，执行Commit操作", xid, branchId);
                try {
                    // 查询事务记录，先锁定事务记录，根据唯一索引xid、branch_id进行查询，产生行锁
                    StorageTxLog storageTxLog = storageTxLogMapper.selectOne(xid, branchId);
                    if (storageTxLog == null) {
                        // 如果事务记录为空，则说明是一个空提交，不允许，终止执行
                        return false;
                    }
                    // 如果事务记录不为空，则继续检查状态
                    if (storageTxLog.getStatus().equals(TccTransactionalStatus.CONFIRM.getCode())) {
                        // 如果状态是已提交，则认为是重复提交，直接返回成功即可
                        return true;
                    }
                    if (storageTxLog.getStatus().equals(TccTransactionalStatus.CANCEL.getCode())) {
                        // 如果状态是已回滚，也是一个异常，一个已回滚的事务，不能重新提交，需要能够拦截到这种异常情况，并报警
                        throw new RuntimeException("请勿重复扣减库存");
                    }
                    // 修改事务日志状态为已提交
                    return storageTxLogMapper.updateStatus(storageTxLog.getXid(), storageTxLog.getBranchId(), TccTransactionalStatus.TRY.getCode(), TccTransactionalStatus.CONFIRM.getCode()) == 1;
                } catch (Exception e) {
                    e.printStackTrace();
                    transactionStatus.setRollbackOnly();
                    return false;
                }
            }
        });
    }

    @Override
    public boolean rollbackDecrease(BusinessActionContext businessActionContext) {
        // 分布式事务ID
        String xid = businessActionContext.getXid();
        long branchId = businessActionContext.getBranchId();
        return transactionTemplate.execute(new TransactionCallback<Boolean>() {
            @Override
            public Boolean doInTransaction(TransactionStatus transactionStatus) {
                log.info("=========xid：{}，branchId：{}，执行Rollback操作", xid, branchId);
                try {
                    // 查询事务记录，先锁定事务记录，根据唯一索引xid、branch_id进行查询，产生行锁
                    StorageTxLog storageTxLog = storageTxLogMapper.selectOne(xid, branchId);
                    // 如果事务记录为空，则认为 Try 方法还没执行，即是空回滚
                    if (storageTxLog == null) {
                        // 允许空回滚，应该先插入一条事务记录，确保后续的 Try 方法不会再执行
                        int result = storageTxLogMapper.insert(new StorageTxLog(xid, branchId, TccTransactionalStatus.CANCEL.getCode(), new Date(), new Date()));
                        if (result != 1) {
                            // 插入失败，则认为 Try 方法正再执行，等待 TC 的重试即可
                            return true;
                        }
                        // 插入成功，则说明 Try 方法还没有执行，空回滚继续执行
                    } else {
                        // 事务记录不为空，则说明 Try 方法已经执行完毕
                        if (storageTxLog.getStatus().equals(TccTransactionalStatus.CONFIRM.getCode())) {
                            // 如果状态为已提交，则同样是一个异常，一个已提交的事务，不能再次回滚
                            throw new RuntimeException("事务已经提交，请勿回滚");
                        }
                        if (storageTxLog.getStatus().equals(TccTransactionalStatus.CANCEL.getCode())) {
                            // 如果状态为已回滚，则说明这是重复调用，允许幂等，直接返回成功即可
                            return true;
                        }
                        // 正常执行 Cancel 逻辑
                        // 修改事务日志状态为已回滚
                        if (storageTxLogMapper.updateStatus(xid, branchId, TccTransactionalStatus.TRY.getCode(), TccTransactionalStatus.CANCEL.getCode()) == 1) {
                            Integer productId = (Integer) businessActionContext.getActionContext("productId");
                            Integer count = (Integer) businessActionContext.getActionContext("count");
                            log.info("商品：{}减库存：{}失败", productId, count);
                            return true;
                        } else {
                            return false;
                        }
                    }
                    return true;
                } catch (Exception e) {
                    e.printStackTrace();
                    transactionStatus.setRollbackOnly();
                    return false;
                }
            }
        });
    }
}
